Document valid pointer and Box transmutations#2282
Conversation
The pointer-to-pointer cast rules and the wide-pointer validity rule both speak of the *metadata* of a pointer, but we hadn't explicitly defined the term (even though we had defined the contents of that metadata). Let's do that and link to it.
We refer to the unsized tail of a type, but we hadn't defined it. Let's do that and link to the definition.
e75977e to
b624dd7
Compare
b624dd7 to
5a667b1
Compare
| <!-- Editor Note: This is nowhere close to an exhaustive list --> | ||
|
|
||
| r[lang-types.box.transmute] | ||
| For types `T` and `U` such that `*mut T` and `*mut U` have the same layout (per [layout.pointer.parametric]) and a [pointer-to-pointer cast] from `*mut T` to `*mut U` is permitted, transmuting a `Box<T>` to a `Box<U>`, where both boxes use the global allocator, is sound when both of the following hold: |
There was a problem hiding this comment.
Just curious, why "global" here?
There was a problem hiding this comment.
Box with a custom allocator is not necessarily going to put the allocator in the same place -- e.g. under -Zrandomize-layout -- for different value types, so I don't think we can make this guarantee. Maybe it can be made for 1-ZST allocators? Not sure...
There was a problem hiding this comment.
Big fan of sticking to just Global here for now. We can talk about the more complex cases later.
(See the similar discussion about how we might be fine in rust-lang/rfcs#3775 to define the field order of *const [T] but are not ready to define the field order of Vec<T>.)
5a667b1 to
6ca17b8
Compare
|
cc @RalfJung |
| The raw pointer types `*const T` and `*const U` have the same layout, as do `*mut T` and `*mut U`, the shared references `&T` and `&U`, and the mutable references `&mut T` and `&mut U`, in each of the following cases: | ||
|
|
||
| - `T` and `U` are both sized types. | ||
| - `T` and `U` both have a [slice] or [`str`] as their [unsized tail]. |
There was a problem hiding this comment.
I find this wording confusing (e.g., are you intending to guarantee that &[u8] and &str have the same layout)?
There was a problem hiding this comment.
I guess you are, based on the test below.
There was a problem hiding this comment.
We already guarantee that:
A reference
&strhas the same layout as a reference&[u8].
This PR further guarantees that pointers with any slice-type metadata have the same layout as pointers with str metadata.
We say that pointers to DSTs store metadata and what that metadata is for pointers to slices, `str`, and trait objects. But a struct or tuple with an unsized tail is itself a DST, and we hadn't said what the metadata is for pointers to these unsized types. Now that we've defined *metadata* and *unsized tail*, let's complete this enumeration.
We guarantee various things about the layout of pointers and references, but we'd made no guarantees of equivalence between two pointers to distinct unsized types that differ in the unsized tail. Let's make some useful guarantees about this.
Our rules for `as` casts document which pointer-to-pointer casts are valid. For unsized types, this validity is based on the compatibility of the pointer metadata. Using this and our layout equivalences, we can define when a pointer transmute will produce the same pointer value as would a cast. Let's define that.
We hadn't documented when it's sound to transmute a `Box<T>` to a `Box<U>`. Now that we've documented when `*mut T` and `*mut U` can be assumed to have the same layout, let's use that to document when this transmute is sound.
The validity rule for the metadata of a wide reference, `Box<T>`, or raw pointer mentions `dyn Trait` and slice but had omitted `str`. Let's fix that.
We document that, for references and `Box<T>`, pointed-to values with slice or `str` metadata must be no larger than `isize::MAX`. We hadn't required this for pointed-to values with `dyn` metadata. It's tempting to think this isn't necessary since we separately require that the metadata point to a vtable generated by the compiler, which ensures the encoded size of the erased type is OK. But the bound is on the total size of the pointed-to value, including any sized prefix of a type with an unsized tail. Since the prefix combined with the size in the vtable can push us past the limit, we need the separate restriction. Let's apply the rule to both cases and add an admonition to remind ourselves of why this is needed.
5891008 to
39a0bd5
Compare
This is best reviewed commit by commit.
Related to:
Boxrust#155597